{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Neural Network Example\n",
    "\n",
    "Build a 2-hidden layers fully connected neural network (a.k.a multilayer perceptron) with TensorFlow v2.\n",
    "\n",
    "This example is using a low-level approach to better understand all mechanics behind building neural networks and the training process.\n",
    "\n",
    "- Author: Aymeric Damien\n",
    "- Project: https://github.com/aymericdamien/TensorFlow-Examples/"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Neural Network Overview\n",
    "\n",
    "<img src=\"http://cs231n.github.io/assets/nn1/neural_net2.jpeg\" alt=\"nn\" style=\"width: 400px;\"/>\n",
    "\n",
    "## MNIST Dataset Overview\n",
    "\n",
    "This example is using MNIST handwritten digits. The dataset contains 60,000 examples for training and 10,000 examples for testing. The digits have been size-normalized and centered in a fixed-size image (28x28 pixels) with values from 0 to 255. \n",
    "\n",
    "In this example, each image will be converted to float32, normalized to [0, 1] and flattened to a 1-D array of 784 features (28*28).\n",
    "\n",
    "![MNIST Dataset](http://neuralnetworksanddeeplearning.com/images/mnist_100_digits.png)\n",
    "\n",
    "More info: http://yann.lecun.com/exdb/mnist/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from __future__ import absolute_import, division, print_function\n",
    "\n",
    "import tensorflow as tf\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# MNIST dataset parameters.\n",
    "num_classes = 10 # total classes (0-9 digits).\n",
    "num_features = 784 # data features (img shape: 28*28).\n",
    "\n",
    "# Training parameters.\n",
    "learning_rate = 0.001\n",
    "training_steps = 3000\n",
    "batch_size = 256\n",
    "display_step = 100\n",
    "\n",
    "# Network parameters.\n",
    "n_hidden_1 = 128 # 1st layer number of neurons.\n",
    "n_hidden_2 = 256 # 2nd layer number of neurons."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Prepare MNIST data.\n",
    "from tensorflow.keras.datasets import mnist\n",
    "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n",
    "# Convert to float32.\n",
    "x_train, x_test = np.array(x_train, np.float32), np.array(x_test, np.float32)\n",
    "# Flatten images to 1-D vector of 784 features (28*28).\n",
    "x_train, x_test = x_train.reshape([-1, num_features]), x_test.reshape([-1, num_features])\n",
    "# Normalize images value from [0, 255] to [0, 1].\n",
    "x_train, x_test = x_train / 255., x_test / 255."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Use tf.data API to shuffle and batch data.\n",
    "train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n",
    "train_data = train_data.repeat().shuffle(5000).batch(batch_size).prefetch(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Store layers weight & bias\n",
    "\n",
    "# A random value generator to initialize weights.\n",
    "random_normal = tf.initializers.RandomNormal()\n",
    "\n",
    "weights = {\n",
    "    'h1': tf.Variable(random_normal([num_features, n_hidden_1])),\n",
    "    'h2': tf.Variable(random_normal([n_hidden_1, n_hidden_2])),\n",
    "    'out': tf.Variable(random_normal([n_hidden_2, num_classes]))\n",
    "}\n",
    "biases = {\n",
    "    'b1': tf.Variable(tf.zeros([n_hidden_1])),\n",
    "    'b2': tf.Variable(tf.zeros([n_hidden_2])),\n",
    "    'out': tf.Variable(tf.zeros([num_classes]))\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create model.\n",
    "def neural_net(x):\n",
    "    # Hidden fully connected layer with 128 neurons.\n",
    "    layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['b1'])\n",
    "    # Apply sigmoid to layer_1 output for non-linearity.\n",
    "    layer_1 = tf.nn.sigmoid(layer_1)\n",
    "    \n",
    "    # Hidden fully connected layer with 256 neurons.\n",
    "    layer_2 = tf.add(tf.matmul(layer_1, weights['h2']), biases['b2'])\n",
    "    # Apply sigmoid to layer_2 output for non-linearity.\n",
    "    layer_2 = tf.nn.sigmoid(layer_2)\n",
    "    \n",
    "    # Output fully connected layer with a neuron for each class.\n",
    "    out_layer = tf.matmul(layer_2, weights['out']) + biases['out']\n",
    "    # Apply softmax to normalize the logits to a probability distribution.\n",
    "    return tf.nn.softmax(out_layer)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Cross-Entropy loss function.\n",
    "def cross_entropy(y_pred, y_true):\n",
    "    # Encode label to a one hot vector.\n",
    "    y_true = tf.one_hot(y_true, depth=num_classes)\n",
    "    # Clip prediction values to avoid log(0) error.\n",
    "    y_pred = tf.clip_by_value(y_pred, 1e-9, 1.)\n",
    "    # Compute cross-entropy.\n",
    "    return tf.reduce_mean(-tf.reduce_sum(y_true * tf.math.log(y_pred)))\n",
    "\n",
    "# Accuracy metric.\n",
    "def accuracy(y_pred, y_true):\n",
    "    # Predicted class is the index of highest score in prediction vector (i.e. argmax).\n",
    "    correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64))\n",
    "    return tf.reduce_mean(tf.cast(correct_prediction, tf.float32), axis=-1)\n",
    "\n",
    "# Stochastic gradient descent optimizer.\n",
    "optimizer = tf.optimizers.SGD(learning_rate)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Optimization process. \n",
    "def run_optimization(x, y):\n",
    "    # Wrap computation inside a GradientTape for automatic differentiation.\n",
    "    with tf.GradientTape() as g:\n",
    "        pred = neural_net(x)\n",
    "        loss = cross_entropy(pred, y)\n",
    "        \n",
    "    # Variables to update, i.e. trainable variables.\n",
    "    trainable_variables = weights.values() + biases.values()\n",
    "\n",
    "    # Compute gradients.\n",
    "    gradients = g.gradient(loss, trainable_variables)\n",
    "    \n",
    "    # Update W and b following gradients.\n",
    "    optimizer.apply_gradients(zip(gradients, trainable_variables))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "step: 100, loss: 567.292969, accuracy: 0.136719\n",
      "step: 200, loss: 398.614929, accuracy: 0.562500\n",
      "step: 300, loss: 226.743774, accuracy: 0.753906\n",
      "step: 400, loss: 193.384521, accuracy: 0.777344\n",
      "step: 500, loss: 138.649963, accuracy: 0.886719\n",
      "step: 600, loss: 109.713669, accuracy: 0.898438\n",
      "step: 700, loss: 90.397217, accuracy: 0.906250\n",
      "step: 800, loss: 104.545380, accuracy: 0.894531\n",
      "step: 900, loss: 94.204697, accuracy: 0.890625\n",
      "step: 1000, loss: 81.660645, accuracy: 0.906250\n",
      "step: 1100, loss: 81.237137, accuracy: 0.902344\n",
      "step: 1200, loss: 65.776703, accuracy: 0.925781\n",
      "step: 1300, loss: 94.195862, accuracy: 0.910156\n",
      "step: 1400, loss: 79.425507, accuracy: 0.917969\n",
      "step: 1500, loss: 93.508163, accuracy: 0.914062\n",
      "step: 1600, loss: 88.912506, accuracy: 0.917969\n",
      "step: 1700, loss: 79.033607, accuracy: 0.929688\n",
      "step: 1800, loss: 65.788315, accuracy: 0.898438\n",
      "step: 1900, loss: 73.462387, accuracy: 0.937500\n",
      "step: 2000, loss: 59.309540, accuracy: 0.917969\n",
      "step: 2100, loss: 67.014008, accuracy: 0.917969\n",
      "step: 2200, loss: 48.297115, accuracy: 0.949219\n",
      "step: 2300, loss: 64.523148, accuracy: 0.910156\n",
      "step: 2400, loss: 72.989517, accuracy: 0.925781\n",
      "step: 2500, loss: 57.588585, accuracy: 0.929688\n",
      "step: 2600, loss: 44.957100, accuracy: 0.960938\n",
      "step: 2700, loss: 59.788242, accuracy: 0.937500\n",
      "step: 2800, loss: 63.581337, accuracy: 0.937500\n",
      "step: 2900, loss: 53.471252, accuracy: 0.941406\n",
      "step: 3000, loss: 43.869728, accuracy: 0.949219\n"
     ]
    }
   ],
   "source": [
    "# Run training for the given number of steps.\n",
    "for step, (batch_x, batch_y) in enumerate(train_data.take(training_steps), 1):\n",
    "    # Run the optimization to update W and b values.\n",
    "    run_optimization(batch_x, batch_y)\n",
    "    \n",
    "    if step % display_step == 0:\n",
    "        pred = neural_net(batch_x)\n",
    "        loss = cross_entropy(pred, batch_y)\n",
    "        acc = accuracy(pred, batch_y)\n",
    "        print(\"step: %i, loss: %f, accuracy: %f\" % (step, loss, acc))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Test Accuracy: 0.936800\n"
     ]
    }
   ],
   "source": [
    "# Test model on validation set.\n",
    "pred = neural_net(x_test)\n",
    "print(\"Test Accuracy: %f\" % accuracy(pred, y_test))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Visualize predictions.\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model prediction: 7\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model prediction: 2\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model prediction: 1\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model prediction: 0\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAP8AAAD8CAYAAAC4nHJkAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADXZJREFUeJzt3X+oXPWZx/HPZ00bMQ2SS0ga0uzeGmVdCW6qF1GUqhRjNlZi0UhCWLJaevtHhRb3jxUVKmpBZJvd/mMgxdAIbdqicQ219AcS1xUWyY2EmvZu2xiyTZqQH6ahiQSquU//uOfKNblzZjJzZs7c+7xfIDNznnNmHo753O85c2bm64gQgHz+pu4GANSD8ANJEX4gKcIPJEX4gaQIP5AU4QeSIvxAUoQfSGpWL1/MNh8nBLosItzKeh2N/LZX2v6t7X22H+nkuQD0ltv9bL/tSyT9TtIdkg5J2iVpXUT8pmQbRn6gy3ox8t8gaV9E7I+Iv0j6oaTVHTwfgB7qJPyLJR2c9PhQsexjbA/bHrE90sFrAahYJ2/4TXVoccFhfURslrRZ4rAf6CedjPyHJC2Z9Pgzkg531g6AXukk/LskXWX7s7Y/KWmtpB3VtAWg29o+7I+ID20/JOnnki6RtCUifl1ZZwC6qu1LfW29GOf8QNf15EM+AKYvwg8kRfiBpAg/kBThB5Ii/EBShB9IivADSRF+ICnCDyRF+IGkCD+QFOEHkiL8QFKEH0iK8ANJEX4gKcIPJEX4gaQIP5AU4QeSIvxAUoQfSIrwA0kRfiApwg8kRfiBpAg/kBThB5Jqe4puSbJ9QNJpSeckfRgRQ1U0hY+77rrrSuvbt29vWBscHKy4m/6xYsWK0vro6GjD2sGDB6tuZ9rpKPyF2yPiRAXPA6CHOOwHkuo0/CHpF7Z32x6uoiEAvdHpYf/NEXHY9gJJv7T9fxHxxuQVij8K/GEA+kxHI39EHC5uj0l6WdINU6yzOSKGeDMQ6C9th9/2HNtzJ+5LWiFpb1WNAeiuTg77F0p62fbE8/wgIn5WSVcAuq7t8EfEfkn/WGEvaODOO+8src+ePbtHnfSXu+++u7T+4IMPNqytXbu26namHS71AUkRfiApwg8kRfiBpAg/kBThB5Kq4lt96NCsWeX/G1atWtWjTqaX3bt3l9YffvjhhrU5c+aUbvv++++31dN0wsgPJEX4gaQIP5AU4QeSIvxAUoQfSIrwA0lxnb8P3H777aX1m266qbT+7LPPVtnOtDFv3rzS+jXXXNOwdtlll5Vuy3V+ADMW4QeSIvxAUoQfSIrwA0kRfiApwg8k5Yjo3YvZvXuxPrJs2bLS+uuvv15af++990rr119/fcPamTNnSredzprtt1tuuaVhbdGiRaXbHj9+vJ2W+kJEuJX1GPmBpAg/kBThB5Ii/EBShB9IivADSRF+IKmm3+e3vUXSFyUdi4hlxbIBST+SNCjpgKT7I+JP3Wtzenv88cdL681+Q37lypWl9Zl6LX9gYKC0fuutt5bWx8bGqmxnxmll5P+epPP/9T0i6bWIuErSa8VjANNI0/BHxBuSTp63eLWkrcX9rZLuqbgvAF3W7jn/wog4IknF7YLqWgLQC13/DT/bw5KGu/06AC5OuyP/UduLJKm4PdZoxYjYHBFDETHU5msB6IJ2w79D0obi/gZJr1TTDoBeaRp+29sk/a+kv7d9yPaXJT0j6Q7bv5d0R/EYwDTS9Jw/ItY1KH2h4l6mrfvuu6+0vmrVqtL6vn37SusjIyMX3dNM8Nhjj5XWm13HL/u+/6lTp9ppaUbhE35AUoQfSIrwA0kRfiApwg8kRfiBpJiiuwJr1qwprTebDvq5556rsp1pY3BwsLS+fv360vq5c+dK608//XTD2gcffFC6bQaM/EBShB9IivADSRF+ICnCDyRF+IGkCD+QFNf5W3T55Zc3rN14440dPfemTZs62n66Gh4u/3W3+fPnl9ZHR0dL6zt37rzonjJh5AeSIvxAUoQfSIrwA0kRfiApwg8kRfiBpLjO36LZs2c3rC1evLh0223btlXdzoywdOnSjrbfu3dvRZ3kxMgPJEX4gaQIP5AU4QeSIvxAUoQfSIrwA0k1vc5ve4ukL0o6FhHLimVPSPqKpOPFao9GxE+71WQ/OH36dMPanj17Sre99tprS+sDAwOl9ZMnT5bW+9mCBQsa1ppNbd7Mm2++2dH22bUy8n9P0soplv9HRCwv/pvRwQdmoqbhj4g3JE3foQfAlDo553/I9q9sb7E9r7KOAPREu+HfJGmppOWSjkj6dqMVbQ/bHrE90uZrAeiCtsIfEUcj4lxEjEn6rqQbStbdHBFDETHUbpMAqtdW+G0vmvTwS5L4ehUwzbRyqW+bpNskzbd9SNI3Jd1me7mkkHRA0le72COALmga/ohYN8Xi57vQS187e/Zsw9q7775buu29995bWn/11VdL6xs3biytd9OyZctK61dccUVpfXBwsGEtItpp6SNjY2MdbZ8dn/ADkiL8QFKEH0iK8ANJEX4gKcIPJOVOL7dc1IvZvXuxHrr66qtL608++WRp/a677iqtl/1seLedOHGitN7s30/ZNNu22+ppwty5c0vrZZdnZ7KIaGnHMvIDSRF+ICnCDyRF+IGkCD+QFOEHkiL8QFJc5+8Dy5cvL61feeWVPerkQi+++GJH22/durVhbf369R0996xZzDA/Fa7zAyhF+IGkCD+QFOEHkiL8QFKEH0iK8ANJcaG0DzSb4rtZvZ/t37+/a8/d7GfF9+5lLpkyjPxAUoQfSIrwA0kRfiApwg8kRfiBpAg/kFTT6/y2l0h6QdKnJY1J2hwR37E9IOlHkgYlHZB0f0T8qXutYjoq+23+Tn+3n+v4nWll5P9Q0r9GxD9IulHS12xfI+kRSa9FxFWSXiseA5gmmoY/Io5ExNvF/dOSRiUtlrRa0sTPtGyVdE+3mgRQvYs657c9KOlzkt6StDAijkjjfyAkLai6OQDd0/Jn+21/StJLkr4REX9u9XzN9rCk4fbaA9AtLY38tj+h8eB/PyK2F4uP2l5U1BdJOjbVthGxOSKGImKoioYBVKNp+D0+xD8vaTQiNk4q7ZC0obi/QdIr1bcHoFtaOey/WdI/S3rH9sR3Sx+V9IykH9v+sqQ/SFrTnRYxnZX9NHwvfzYeF2oa/oh4U1KjE/wvVNsOgF7hE35AUoQfSIrwA0kRfiApwg8kRfiBpPjpbnTVpZde2va2Z8+erbATnI+RH0iK8ANJEX4gKcIPJEX4gaQIP5AU4QeS4jo/uuqBBx5oWDt16lTptk899VTV7WASRn4gKcIPJEX4gaQIP5AU4QeSIvxAUoQfSIrr/OiqXbt2Naxt3LixYU2Sdu7cWXU7mISRH0iK8ANJEX4gKcIPJEX4gaQIP5AU4QeScrM50m0vkfSCpE9LGpO0OSK+Y/sJSV+RdLxY9dGI+GmT52JCdqDLIsKtrNdK+BdJWhQRb9ueK2m3pHsk3S/pTET8e6tNEX6g+1oNf9NP+EXEEUlHivunbY9KWtxZewDqdlHn/LYHJX1O0lvFoods/8r2FtvzGmwzbHvE9khHnQKoVNPD/o9WtD8l6b8lfSsittteKOmEpJD0lMZPDR5s8hwc9gNdVtk5vyTZ/oSkn0j6eURc8G2M4ojgJxGxrMnzEH6gy1oNf9PDftuW9Lyk0cnBL94InPAlSXsvtkkA9Wnl3f5bJP2PpHc0fqlPkh6VtE7Sco0f9h+Q9NXizcGy52LkB7qs0sP+qhB+oPsqO+wHMDMRfiApwg8kRfiBpAg/kBThB5Ii/EBShB9IivADSRF+ICnCDyRF+IGkCD+QFOEHkur1FN0nJP3/pMfzi2X9qF9769e+JHprV5W9/V2rK/b0+/wXvLg9EhFDtTVQol9769e+JHprV129cdgPJEX4gaTqDv/mml+/TL/21q99SfTWrlp6q/WcH0B96h75AdSklvDbXmn7t7b32X6kjh4asX3A9ju299Q9xVgxDdox23snLRuw/Uvbvy9up5wmrabenrD9x2Lf7bG9qqbeltjeaXvU9q9tf71YXuu+K+mrlv3W88N+25dI+p2kOyQdkrRL0rqI+E1PG2nA9gFJQxFR+zVh25+XdEbSCxOzIdl+VtLJiHim+MM5LyL+rU96e0IXOXNzl3prNLP0v6jGfVfljNdVqGPkv0HSvojYHxF/kfRDSatr6KPvRcQbkk6et3i1pK3F/a0a/8fTcw166wsRcSQi3i7un5Y0MbN0rfuupK9a1BH+xZIOTnp8SP015XdI+oXt3baH625mCgsnZkYqbhfU3M/5ms7c3EvnzSzdN/uunRmvq1ZH+KeaTaSfLjncHBHXSfonSV8rDm/Rmk2Slmp8Grcjkr5dZzPFzNIvSfpGRPy5zl4mm6KvWvZbHeE/JGnJpMefkXS4hj6mFBGHi9tjkl7W+GlKPzk6MUlqcXus5n4+EhFHI+JcRIxJ+q5q3HfFzNIvSfp+RGwvFte+76bqq679Vkf4d0m6yvZnbX9S0lpJO2ro4wK25xRvxMj2HEkr1H+zD++QtKG4v0HSKzX28jH9MnNzo5mlVfO+67cZr2v5kE9xKeM/JV0iaUtEfKvnTUzB9hUaH+2l8W88/qDO3mxvk3Sbxr/1dVTSNyX9l6QfS/pbSX+QtCYiev7GW4PebtNFztzcpd4azSz9lmrcd1XOeF1JP3zCD8iJT/gBSRF+ICnCDyRF+IGkCD+QFOEHkiL8QFKEH0jqr8DO4JozFB6IAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model prediction: 4\n"
     ]
    }
   ],
   "source": [
    "# Predict 5 images from validation set.\n",
    "n_images = 5\n",
    "test_images = x_test[:n_images]\n",
    "predictions = neural_net(test_images)\n",
    "\n",
    "# Display image and model prediction.\n",
    "for i in range(n_images):\n",
    "    plt.imshow(np.reshape(test_images[i], [28, 28]), cmap='gray')\n",
    "    plt.show()\n",
    "    print(\"Model prediction: %i\" % np.argmax(predictions.numpy()[i]))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
